#ifndef _MESH_H
#define _MESH_H

#include "Scene.h"
#include "Texture.h"
#include "vObject.h"

class Scene;
class Texture;
class vObject;
class Mesh;
class D3D11Effect;

//========================================================================
//			Structs
//========================================================================

struct Light_spec {
	D3DXCOLOR ambient;
	D3DXCOLOR diffuse;
	D3DXCOLOR specular;
	D3DXVECTOR4 PosW;
	D3DXVECTOR4 DirW;
	D3DXVECTOR4 attanuation;
	D3DXVECTOR2 param;
	UINT32 type;
	float reserved;		//112
};

//updates once per frame in InitRender()
struct cb_VSCB_per_frame {
	UINT32 HazeMode;
	D3DXVECTOR3 FogColor;	
	float FogDensity;
};

//updates in vVessel::Render();
struct cb_VSCB_per_object {
	D3DXMATRIX VP;
	Sun_spec Sun;
	UINT32 ActiveLightsCount;
	float waste1[3];
};

//updates in Render()
struct cb_VSCB_per_group {
	D3DXMATRIX W;
	float spec_power;
	float waste2[3];
};

struct cb_PSCB_per_object{
	Sun_spec Sun;		//64
};

 struct cb_VSCB_BaseTile {
	D3DXMATRIX VP;			//64

	UINT32 HazeMode;
	D3DXVECTOR3 FogColor;	//80

	D3DXVECTOR3 SunDir;
	float SunAppRad;		//96

	float Ambient0;
	float GlobalAmb;
	float FogDensity;
	float Dispersion;		//112
};

//========================================================================
//			Mesh
//========================================================================

#define MAXTEX 1UL

typedef struct MESH_VERTEX {
	D3DXVECTOR3 pos;	//12
	D3DXVECTOR3 norm;	//24
	D3DXVECTOR3 tang;	//36
	D3DXVECTOR2 tex;	//44
} MESH_VERTEX;

typedef struct D3D11Material {
	D3DXVECTOR3 ambient;
	float opacity;			//16

	D3DXVECTOR3 diffuse;
	UINT32 flags;		//32

	D3DXVECTOR3 specular;
	float spec_power;		//48

	D3DXVECTOR3 emissive;
	float TexEx_mix;		//64	spower4
} D3D11Material;

typedef struct MGROUP {
	DWORD
		svtx,		//vertex offset
		nvtx,		//vertex count
		sidx,		//index offset
		nidx,		//index count
		nface,		//trangle count
		mat_idx,	//material index
		tex_idx,	//diffuse texture index
		norm_idx,	//norm map idx
		userflag;

	bool
		upd_once;		//T matrix was updated at least once

	WORD BlendMode;//true for transparent groups
	D3DXMATRIX
		T,			//current frame
		T_new;		//next frame

	D3DXVECTOR3 bsPos;	//bounding sphere pos
	float
		bsRad,
		scale,
		scale_new;	//bounding sphere rad

	D3DXVECTOR3 AABB[2];//axial aligned bounding box
	D3DXVECTOR3 bbPos;
	float bbRad;

	DWORD tex_idx_ex[MAXTEX];	//???
	float tex_mix_idx[MAXTEX];	//???
} MGROUP;

//==========================================================================
//			Mesh loaded by oapiLoadMeshGlobal()
//==========================================================================

class D3D11TPLMesh {
	friend class MeshManager;
	friend class D3D11Mesh;
public:
	D3D11TPLMesh( MESHHANDLE Mesh );
	D3D11TPLMesh();
	~D3D11TPLMesh();

	bool SetMeshTexture( DWORD tex_idx, Texture *tex );
	int SetMeshMaterial( DWORD mat_idx, const MATERIAL *mat );
	bool SetMeshProperty( DWORD prop, DWORD value );
	virtual int EditGroup( DWORD grp, GROUPEDITSPEC *ges );
protected:
	void LoadGroups( MESHGROUPEX **grps );
	void LoadMaterial( D3D11Material &M, const MATERIAL *mat );
	void ProcessInherit();
	void FindTransparent();
	void SearchForNormalMaps();
	void ComputeTangents( MGROUP *GR );
	void ComputeBoundingBox( MGROUP &G );
	bool FormatNormalFileName(Texture * diffuseTexture, char * nname);

	MGROUP *GR;			//mesh groups
	UINT ngrp;			//mesh group count

	D3D11Material *MAT;	//materials
	Texture
		**TEX,	//array of textures		.dds
		**NORM,	//normal/bump maps		_norm.dds
		**SPEC,	//specular maps			_spec.dds
		**EMIS;	//emissive map			_emis.dds
	UINT
		nmat,
		ntex,			//size of TEX and NORM arrays
		nnorm,			//how many loaded normal/bump maps have this mesh
		nspec,			//how many loaded specular maps have this mesh
		nemis;			//how many loaded emissive maps have this mesh

	MESH_VERTEX *_VB;	//system memory copy of vertex buffer
	D3DXVECTOR3 *_shadow_VB;
	DWORD *_IB;			//system memory copy of index buffer
	DWORD *_shadow_IB;

	D3DXVECTOR3 bsPos;
	float bsRad;

	D3DXVECTOR3 AABB[2];
	bool bRecomputeBoundings, bMixMatAlpha;

	UINT
		vbsize,			//size of vertex buffer
		ibsize;			//size of index buffer
};

//==========================================================================
//			Mesh for rendering (asteroids, bases, vessels)
//==========================================================================

class D3D11Mesh : public D3D11TPLMesh 
{
	friend class Scene;
	friend class vBase;
	friend class vVessel;
	friend class vPlanet;
	friend class D3D11ParticleStream;
public:
//mesh from pre-loaded template:
	D3D11Mesh( D3D11TPLMesh *tpl );
//generic meshes:
	D3D11Mesh( MESHHANDLE Mesh, bool shadows );
//base tiles:
	D3D11Mesh( DWORD _ngrp, MESHGROUPEX **grps, SURFHANDLE *texs );
	~D3D11Mesh();

	static void GlobalInit(D3D11Client * gc);
	static void GlobalExit();

	void SetTextureMix( DWORD ntex, float mix );

	void TransformGroup( UINT grp, D3DXMATRIX *T );
	void TransformAll( D3DXMATRIX *T );
	void UpdateBoundings();
	void ResetTransforms();

	void Update();

	//base tiles
	static void InitRenderBaseTiles();
	void RenderBaseTiles( vObject *vobj, D3DXMATRIX *mWorld );

	//vessel and base meshes
	static void InitRender();
	void Render( vObject *vobj, D3DXMATRIX *mOfsWorld, bool mesh_cull = true, bool grp_cull = true );
	void RenderVC( vObject *vobj, D3DXMATRIX *mOfsWorld, DWORD nmsh, bool mesh_cull = true, bool grp_cull = true );
	
	//mesh bounding box
	void GetBoundingBox( D3DXVECTOR3 *BB );//base craters
	void RenderBoundingBox( D3DXMATRIX *mOfsWorld );

	//transparent groups [to do]
	static void InitRenderTransparent();
	void RenderTransparent( vObject *vobj, D3DXMATRIX *mOfsWorld, Sun_spec *SunLight = NULL  );

	//vessel and building shadows
	static void InitRenderShadows();
	void RenderShadows( D3DXMATRIX *mOfsProj );

	int EditGroup( DWORD grp, GROUPEDITSPEC *ges );
private:
	void CreateBuffers( bool shadows = false );

	ID3D11Buffer
		*shadow_VB,	//shadow geometry
		*shadow_IB,	//shadow index buffer (32-bit)
		*VB,		//GPU read-only vertex buffer
		*IB;		//GPU read-only index buffer

	static CComPtrEx<ID3D11InputLayout> IL_MESH_VERTEX;

	static ID3D11InputLayout
		*IL_D3DXVECTOR3;		//simplified shadow vertex
		//*IL_MESH_VERTEX;		//full vertex

	static CComPtrEx<ID3D11VertexShader> VS_Mesh;

	static ID3D11VertexShader
		//*VS_Mesh,				//Mesh.fx
		*VS_VC,					//VC.fx
		*VS_Shadow,				//Mesh_Shadows.fx
		*VS_BaseTile;			//Mesh_BaseTile.fx

	static CComPtrEx<ID3D11PixelShader> PS_Mesh;
	static ID3D11PixelShader
		//*PS_Mesh,
		*PS_VC[3],				//0,1 mesh, 2 mfd, 3 hud
		*PS_Shadow,				//vessel/buliding's shadows
		*PS_BaseTile;			//base tiles

	static ID3D11SamplerState
		*SS_BaseTile,	//base tile texture
		*SS_Texture,	//texture sampler
		*SS_HUD;		//normal map sampler

	static ID3D11DepthStencilState
		*DSS_Shadow,
		*DSS_Mesh;

	static ID3D11Buffer
		*cb_VS_VC_per_frame,
		*cb_VS_per_frame,		//fog
		*cb_VS_per_object,	//lights + sun
		*cb_VS_per_group,	//W, WVP + spec_power
		*cb_PS_per_object,	//sun lights params
		*cb_PS_per_group,	//material
		*cb_VS_BaseTile;	//base tile atm consts
	
	static const UINT
		MVertexStride,
		VBOffset;

	static const float blend_factors[4];
	DWORD pShaderType;

//constant buffer data structs
	static cb_VSCB_per_frame VSCB_per_frame;
	static cb_VSCB_per_object VSCB_per_object;
	static cb_VSCB_per_group VSCB_per_group;
	static cb_PSCB_per_object PSCB_per_object;
	static cb_VSCB_BaseTile VSCB_BaseTile;

	static D3D11Material
		DefaultMat;
	static Sun_spec
		WhiteSunLight;

	bool bMeshT;
	D3DXMATRIX mT, mT_new;	//mesh transformation matrix
};

class MeshManager {
public:
	MeshManager();
	~MeshManager();

	void Store( MESHHANDLE hMesh );
	D3D11TPLMesh *Find( MESHHANDLE hMesh );
	void UpdateAMSOMesh( MESHHANDLE hMesh );
private:	
	struct MESHTPL {
		D3D11TPLMesh *TMesh;
		MESHHANDLE mesh;
	} *MSH;
	DWORD
		nmesh,
		maxmesh;
};

extern MeshManager *MM;

#endif